﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Globalization;

namespace WeakScript
{
    class Executor
    {
        public delegate bool FunctionReceive(string name, List<WeakVar> args);

        public FunctionReceive OnFunctionCall;
        readonly public OperationRoom OperationRoom;

        public long com_num;

        public Executor()
        {
            this.OperationRoom = new OperationRoom();
        }

        public void ExecuteList(List<string> codeLines)
        {
            com_num = 0;
            foreach (var item in codeLines)
            {
                com_num++;
                ExecuteLine(item);
            }
        }

        public void ExecuteLine(string str)
        {
            string command = str.Split(' ')[0];
            string args = str.Split(' ')[1];

            if (command.StartsWith("push"))//expression const
            {
                if (command.EndsWith("int"))
                {
                    WeakVar var = OperationRoom.CreateBlankVar();
                    var.InnerType = WeakVar.InternalType.FLT;
                    var.floatValue = Double.Parse(args, CultureInfo.InvariantCulture);
                    var.Name = "const_flt" + Global.GetUK();
                    OperationRoom.PushVar(var);
                }
                else if (command.EndsWith("str"))
                {
                    WeakVar var = OperationRoom.CreateBlankVar();
                    var.InnerType = WeakVar.InternalType.STR;
                    var.Name = "const_str" + Global.GetUK();

                    var.stringValue = args.Replace("\\\"", "\"");
                    var.stringValue = var.stringValue.Substring(1, var.stringValue.Length - 1);
                    OperationRoom.PushVar(var);
                }
                else if (command.EndsWith("float"))
                {
                    WeakVar var = OperationRoom.CreateBlankVar();
                    var.InnerType = WeakVar.InternalType.FLT;
                    var.floatValue = Double.Parse(args, CultureInfo.InvariantCulture);
                    var.Name = "const_flt" + Global.GetUK();

                    OperationRoom.PushVar(var);
                }
                else if (command.EndsWith("var"))
                {
                    WeakVar var = OperationRoom.GetNamedHeapVar(args);
                    if (var != null)
                    {
                        OperationRoom.PushVar(var);
                    }
                    else
                    {
                        throw new Exception("Unknown var:" + args);
                    }
                }
            }
            else if (command.StartsWith("b_new"))//begin declaration
            {
                OperationRoom.PushMarker("b_new");
            }
            else if (command.StartsWith("new"))//create var
            {
                OperationRoom.PushVar(OperationRoom.CreateNamedHeapVar(args));
            }
            else if (command.StartsWith("typed"))//type declared
            {
                if (!Registr.TypesMap.Keys.Contains<string>(args))
                    throw new Exception("Unknown type:" + args);
                
                while (OperationRoom.PeekVar().InnerType != WeakVar.InternalType.MRK)
                {
                    OperationRoom.SetType(OperationRoom.PopVar(), args);
                }
            }
            else if (command.StartsWith("e_new"))//end declaration
            {
                while (OperationRoom.PopVar().InnerType != WeakVar.InternalType.MRK) { }
            }
            else if (command.StartsWith("set"))//set var
            {
                WeakVar var = OperationRoom.GetNamedHeapVar(args);
                if (var != null)
                {
                    OperationRoom.Set(var, OperationRoom.PopVar());
                }
                else
                {
                    throw new Exception("Unknown var:" + args);
                }
            }
            else if (command.EndsWith("_op"))
            {
                if (args == "*")
                {
                    OperationRoom.ExecuteBinaryOperation(Operations.Mul);
                }
                else
                    if (args == "/")
                    {
                        OperationRoom.ExecuteBinaryOperation(Operations.Div);
                    }
                    else
                        if (args == "+")
                        {
                            OperationRoom.ExecuteBinaryOperation(Operations.Sum);
                        }
                        else
                            if (args == "-")
                            {
                                OperationRoom.ExecuteBinaryOperation(Operations.Sub);
                            }
                            else
                                if (args == "neg")
                                {
                                    WeakVar var = OperationRoom.PopVar();
                                    WeakVar zero = new WeakVar() { InnerType = var.InnerType };
                                    OperationRoom.PushVar(zero);
                                    OperationRoom.PushVar(var);
                                    OperationRoom.ExecuteBinaryOperation(Operations.Sub);
                                }
            }
            else if (command.StartsWith("func_"))
            {
                if (command.EndsWith("b_args"))
                {
                    OperationRoom.PushMarker(args);
                }
                else if (command.EndsWith("arg_copy"))
                {
                    WeakVar var = OperationRoom.CreateTempVar();
                    OperationRoom.Set(var, OperationRoom.PopVar());
                    OperationRoom.PushVar(var);
                }
                else if (command.EndsWith("e_args"))
                {

                }
                else if (command.EndsWith("post_call"))
                {
                    List<WeakVar> arguments = new List<WeakVar>();
                    WeakVar var = OperationRoom.PopVar();
                    while (var.InnerType != WeakVar.InternalType.MRK)
                    {
                        arguments.Add(var);
                        var = OperationRoom.PopVar();
                    }

                    if (OnFunctionCall != null){
                        bool resolved = OnFunctionCall(args, arguments);
                        if (!resolved)
                        {
                            WeakFunctionDescriptor funcscript = new WeakFunctionDescriptor("testfunc");
                            OperationRoom.SaveAndLeaveNamespace();
                            funcscript.PrepareUtiliteVars(arguments);
                            ExecuteList(funcscript.ScriptBlock.codeLines);
                            funcscript.CloseUtiliteVars(arguments);
                            OperationRoom.RestoreNamespace();
                        }
                    }                        
                }
            }
        }
    }
}
